// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu/command_buffer/service/gles2_cmd_decoder.h"

#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/service/cmd_buffer_engine.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/context_state.h"
#include "gpu/command_buffer/service/gl_surface_mock.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h"

#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/image_manager.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/mocks.h"
#include "gpu/command_buffer/service/program_manager.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_mock.h"
#include "ui/gl/gl_surface_stub.h"

#if !defined(GL_DEPTH24_STENCIL8)
#define GL_DEPTH24_STENCIL8 0x88F0
#endif

using ::gfx::MockGLInterface;
using ::testing::_;
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::MatcherCast;
using ::testing::Mock;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
using ::testing::SetArrayArgument;
using ::testing::StrEq;
using ::testing::StrictMock;

namespace gpu {
namespace gles2 {

    using namespace cmds;

    class GLES2DecoderTestWithExtensionsOnGLES2 : public GLES2DecoderTest {
    public:
        GLES2DecoderTestWithExtensionsOnGLES2() { }

        void SetUp() override { }
        void Init(const char* extensions)
        {
            InitState init;
            init.extensions = extensions;
            init.gl_version = "opengl es 2.0";
            init.has_alpha = true;
            init.has_depth = true;
            init.request_alpha = true;
            init.request_depth = true;
            InitDecoder(init);
        }
    };

    TEST_P(GLES2DecoderTest, CheckFramebufferStatusWithNoBoundTarget)
    {
        EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(_)).Times(0);
        CheckFramebufferStatus::Result* result = static_cast<CheckFramebufferStatus::Result*>(shared_memory_address_);
        *result = 0;
        CheckFramebufferStatus cmd;
        cmd.Init(GL_FRAMEBUFFER, shared_memory_id_, shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), *result);
    }

    TEST_P(GLES2DecoderWithShaderTest, BindAndDeleteFramebuffer)
    {
        SetupTexture();
        AddExpectationsForSimulatedAttrib0(kNumVertices, 0);
        SetupExpectationsForApplyingDefaultDirtyState();
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoDeleteFramebuffer(client_framebuffer_id_,
            kServiceFramebufferId,
            true,
            GL_FRAMEBUFFER,
            0,
            true,
            GL_FRAMEBUFFER,
            0);
        EXPECT_CALL(*gl_, DrawArrays(GL_TRIANGLES, 0, kNumVertices))
            .Times(1)
            .RetiresOnSaturation();
        DrawArrays cmd;
        cmd.Init(GL_TRIANGLES, 0, kNumVertices);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES2DecoderTest, FramebufferRenderbufferWithNoBoundTarget)
    {
        EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(_, _, _, _)).Times(0);
        FramebufferRenderbuffer cmd;
        cmd.Init(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_RENDERBUFFER,
            client_renderbuffer_id_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
    }

    TEST_P(GLES2DecoderTest, FramebufferTexture2DWithNoBoundTarget)
    {
        EXPECT_CALL(*gl_, FramebufferTexture2DEXT(_, _, _, _, _)).Times(0);
        FramebufferTexture2D cmd;
        cmd.Init(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            client_texture_id_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
    }

    TEST_P(GLES2DecoderTest, GetFramebufferAttachmentParameterivWithNoBoundTarget)
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetFramebufferAttachmentParameterivEXT(_, _, _, _))
            .Times(0);
        GetFramebufferAttachmentParameteriv cmd;
        cmd.Init(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
            shared_memory_id_,
            shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
    }

    TEST_P(GLES2DecoderTest, GetFramebufferAttachmentParameterivWithRenderbuffer)
    {
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        EXPECT_CALL(*gl_, GetError())
            .WillRepeatedly(Return(GL_NO_ERROR));
        EXPECT_CALL(*gl_,
            FramebufferRenderbufferEXT(GL_FRAMEBUFFER,
                GL_COLOR_ATTACHMENT0,
                GL_RENDERBUFFER,
                kServiceRenderbufferId))
            .Times(1)
            .RetiresOnSaturation();
        GetFramebufferAttachmentParameteriv::Result* result = static_cast<GetFramebufferAttachmentParameteriv::Result*>(
            shared_memory_address_);
        result->size = 0;
        const GLint* result_value = result->GetData();
        FramebufferRenderbuffer fbrb_cmd;
        GetFramebufferAttachmentParameteriv cmd;
        fbrb_cmd.Init(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_RENDERBUFFER,
            client_renderbuffer_id_);
        cmd.Init(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
            shared_memory_id_,
            shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(fbrb_cmd));
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(client_renderbuffer_id_, static_cast<GLuint>(*result_value));
    }

    TEST_P(GLES2DecoderTest, GetFramebufferAttachmentParameterivWithTexture)
    {
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        EXPECT_CALL(*gl_, GetError())
            .WillRepeatedly(Return(GL_NO_ERROR));
        EXPECT_CALL(*gl_,
            FramebufferTexture2DEXT(GL_FRAMEBUFFER,
                GL_COLOR_ATTACHMENT0,
                GL_TEXTURE_2D,
                kServiceTextureId,
                0))
            .Times(1)
            .RetiresOnSaturation();
        GetFramebufferAttachmentParameteriv::Result* result = static_cast<GetFramebufferAttachmentParameteriv::Result*>(
            shared_memory_address_);
        result->SetNumResults(0);
        const GLint* result_value = result->GetData();
        FramebufferTexture2D fbtex_cmd;
        GetFramebufferAttachmentParameteriv cmd;
        fbtex_cmd.Init(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            client_texture_id_);
        cmd.Init(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
            shared_memory_id_,
            shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(fbtex_cmd));
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(client_texture_id_, static_cast<GLuint>(*result_value));
    }

    TEST_P(GLES2DecoderWithShaderTest,
        GetRenderbufferParameterivRebindRenderbuffer)
    {
        SetupTexture();
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        DoRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, GL_RGBA, 1, 1, GL_NO_ERROR);

        GetRenderbufferParameteriv cmd;
        cmd.Init(GL_RENDERBUFFER,
            GL_RENDERBUFFER_RED_SIZE,
            shared_memory_id_,
            shared_memory_offset_);

        RestoreRenderbufferBindings();
        EnsureRenderbufferBound(true);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_,
            GetRenderbufferParameterivEXT(
                GL_RENDERBUFFER, GL_RENDERBUFFER_RED_SIZE, _));
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES2DecoderTest, GetRenderbufferParameterivWithNoBoundTarget)
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetRenderbufferParameterivEXT(_, _, _)).Times(0);
        GetRenderbufferParameteriv cmd;
        cmd.Init(GL_RENDERBUFFER,
            GL_RENDERBUFFER_WIDTH,
            shared_memory_id_,
            shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
    }

    TEST_P(GLES2DecoderWithShaderTest, RenderbufferStorageRebindRenderbuffer)
    {
        SetupTexture();
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        RestoreRenderbufferBindings();
        EnsureRenderbufferBound(true);
        DoRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, GL_RGBA, 1, 1, GL_NO_ERROR);
    }

    TEST_P(GLES2DecoderTest, RenderbufferStorageWithNoBoundTarget)
    {
        EXPECT_CALL(*gl_, RenderbufferStorageEXT(_, _, _, _)).Times(0);
        RenderbufferStorage cmd;
        cmd.Init(GL_RENDERBUFFER, GL_RGBA4, 3, 4);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
    }

    namespace {

        // A class to emulate glReadPixels
        class ReadPixelsEmulator {
        public:
            // pack_alignment is the alignment you want ReadPixels to use
            // when copying. The actual data passed in pixels should be contiguous.
            ReadPixelsEmulator(GLsizei width,
                GLsizei height,
                GLint bytes_per_pixel,
                const void* src_pixels,
                const void* expected_pixels,
                GLint pack_alignment)
                : width_(width)
                , height_(height)
                , pack_alignment_(pack_alignment)
                , bytes_per_pixel_(bytes_per_pixel)
                , src_pixels_(reinterpret_cast<const int8*>(src_pixels))
                , expected_pixels_(reinterpret_cast<const int8*>(expected_pixels))
            {
            }

            void ReadPixels(GLint x,
                GLint y,
                GLsizei width,
                GLsizei height,
                GLenum format,
                GLenum type,
                void* pixels) const
            {
                DCHECK_GE(x, 0);
                DCHECK_GE(y, 0);
                DCHECK_LE(x + width, width_);
                DCHECK_LE(y + height, height_);
                for (GLint yy = 0; yy < height; ++yy) {
                    const int8* src = GetPixelAddress(src_pixels_, x, y + yy);
                    const void* dst = ComputePackAlignmentAddress(0, yy, width, pixels);
                    memcpy(const_cast<void*>(dst), src, width * bytes_per_pixel_);
                }
            }

            bool CompareRowSegment(GLint x,
                GLint y,
                GLsizei width,
                const void* data) const
            {
                DCHECK(x + width <= width_ || width == 0);
                return memcmp(data,
                           GetPixelAddress(expected_pixels_, x, y),
                           width * bytes_per_pixel_)
                    == 0;
            }

            // Helper to compute address of pixel in pack aligned data.
            const void* ComputePackAlignmentAddress(GLint x,
                GLint y,
                GLsizei width,
                const void* address) const
            {
                GLint unpadded_row_size = ComputeImageDataSize(width, 1);
                GLint two_rows_size = ComputeImageDataSize(width, 2);
                GLsizei padded_row_size = two_rows_size - unpadded_row_size;
                GLint offset = y * padded_row_size + x * bytes_per_pixel_;
                return static_cast<const int8*>(address) + offset;
            }

            GLint ComputeImageDataSize(GLint width, GLint height) const
            {
                GLint row_size = width * bytes_per_pixel_;
                if (height > 1) {
                    GLint temp = row_size + pack_alignment_ - 1;
                    GLint padded_row_size = (temp / pack_alignment_) * pack_alignment_;
                    GLint size_of_all_but_last_row = (height - 1) * padded_row_size;
                    return size_of_all_but_last_row + row_size;
                } else {
                    return height * row_size;
                }
            }

        private:
            const int8* GetPixelAddress(const int8* base, GLint x, GLint y) const
            {
                return base + (width_ * y + x) * bytes_per_pixel_;
            }

            GLsizei width_;
            GLsizei height_;
            GLint pack_alignment_;
            GLint bytes_per_pixel_;
            const int8* src_pixels_;
            const int8* expected_pixels_;
        };

    } // anonymous namespace

    void GLES2DecoderTest::CheckReadPixelsOutOfRange(GLint in_read_x,
        GLint in_read_y,
        GLsizei in_read_width,
        GLsizei in_read_height,
        bool init)
    {
        const GLsizei kWidth = 5;
        const GLsizei kHeight = 3;
        const GLint kBytesPerPixel = 4;
        const GLint kPackAlignment = 4;
        const GLenum kFormat = GL_RGBA;
        static const uint8 kSrcPixels[kWidth * kHeight * kBytesPerPixel] = {
            12, 13, 14, 255, 18, 19, 18, 255, 19, 12, 13, 255, 14, 18, 19, 255,
            18, 19, 13, 255, 29, 28, 23, 255, 22, 21, 22, 255, 21, 29, 28, 255,
            23, 22, 21, 255, 22, 21, 28, 255, 31, 34, 39, 255, 37, 32, 37, 255,
            32, 31, 34, 255, 39, 37, 32, 255, 37, 32, 34, 255
        };

        ClearSharedMemory();

        // We need to setup an FBO so we can know the max size that ReadPixels will
        // access
        if (init) {
            DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
            DoTexImage2D(GL_TEXTURE_2D,
                0,
                kFormat,
                kWidth,
                kHeight,
                0,
                kFormat,
                GL_UNSIGNED_BYTE,
                kSharedMemoryId,
                kSharedMemoryOffset);
            DoBindFramebuffer(
                GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
            DoFramebufferTexture2D(GL_FRAMEBUFFER,
                GL_COLOR_ATTACHMENT0,
                GL_TEXTURE_2D,
                client_texture_id_,
                kServiceTextureId,
                0,
                GL_NO_ERROR);
            EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_FRAMEBUFFER))
                .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
                .RetiresOnSaturation();
        }

        ReadPixelsEmulator emu(
            kWidth, kHeight, kBytesPerPixel, kSrcPixels, kSrcPixels, kPackAlignment);
        typedef ReadPixels::Result Result;
        Result* result = GetSharedMemoryAs<Result*>();
        uint32 result_shm_id = kSharedMemoryId;
        uint32 result_shm_offset = kSharedMemoryOffset;
        uint32 pixels_shm_id = kSharedMemoryId;
        uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result);
        void* dest = &result[1];
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        // ReadPixels will be called for valid size only even though the command
        // is requesting a larger size.
        GLint read_x = std::max(0, in_read_x);
        GLint read_y = std::max(0, in_read_y);
        GLint read_end_x = std::max(0, std::min(kWidth, in_read_x + in_read_width));
        GLint read_end_y = std::max(0, std::min(kHeight, in_read_y + in_read_height));
        GLint read_width = read_end_x - read_x;
        GLint read_height = read_end_y - read_y;
        if (read_width > 0 && read_height > 0) {
            for (GLint yy = read_y; yy < read_end_y; ++yy) {
                EXPECT_CALL(
                    *gl_,
                    ReadPixels(read_x, yy, read_width, 1, kFormat, GL_UNSIGNED_BYTE, _))
                    .WillOnce(Invoke(&emu, &ReadPixelsEmulator::ReadPixels))
                    .RetiresOnSaturation();
            }
        }
        ReadPixels cmd;
        cmd.Init(in_read_x,
            in_read_y,
            in_read_width,
            in_read_height,
            kFormat,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));

        GLint unpadded_row_size = emu.ComputeImageDataSize(in_read_width, 1);
        scoped_ptr<int8[]> zero(new int8[unpadded_row_size]);
        scoped_ptr<int8[]> pack(new int8[kPackAlignment]);
        memset(zero.get(), 0, unpadded_row_size);
        memset(pack.get(), kInitialMemoryValue, kPackAlignment);
        for (GLint yy = 0; yy < in_read_height; ++yy) {
            const int8* row = static_cast<const int8*>(
                emu.ComputePackAlignmentAddress(0, yy, in_read_width, dest));
            GLint y = in_read_y + yy;
            if (y < 0 || y >= kHeight) {
                EXPECT_EQ(0, memcmp(zero.get(), row, unpadded_row_size));
            } else {
                // check off left.
                GLint num_left_pixels = std::max(-in_read_x, 0);
                GLint num_left_bytes = num_left_pixels * kBytesPerPixel;
                EXPECT_EQ(0, memcmp(zero.get(), row, num_left_bytes));

                // check off right.
                GLint num_right_pixels = std::max(in_read_x + in_read_width - kWidth, 0);
                GLint num_right_bytes = num_right_pixels * kBytesPerPixel;
                EXPECT_EQ(0,
                    memcmp(zero.get(),
                        row + unpadded_row_size - num_right_bytes,
                        num_right_bytes));

                // check middle.
                GLint x = std::max(in_read_x, 0);
                GLint num_middle_pixels = std::max(in_read_width - num_left_pixels - num_right_pixels, 0);
                EXPECT_TRUE(
                    emu.CompareRowSegment(x, y, num_middle_pixels, row + num_left_bytes));
            }

            // check padding
            if (yy != in_read_height - 1) {
                GLint temp = unpadded_row_size + kPackAlignment - 1;
                GLint padded_row_size = (temp / kPackAlignment) * kPackAlignment;
                GLint num_padding_bytes = padded_row_size - unpadded_row_size;
                if (num_padding_bytes) {
                    EXPECT_EQ(0, memcmp(pack.get(), row + unpadded_row_size, num_padding_bytes));
                }
            }
        }
    }

    TEST_P(GLES2DecoderTest, ReadPixels)
    {
        const GLsizei kWidth = 5;
        const GLsizei kHeight = 3;
        const GLint kBytesPerPixel = 4;
        const GLint kPackAlignment = 4;
        static const uint8 kSrcPixels[kWidth * kHeight * kBytesPerPixel] = {
            12, 13, 14, 255, 18, 19, 18, 255, 19, 12, 13, 255, 14, 18, 19, 255,
            18, 19, 13, 255, 29, 28, 23, 255, 22, 21, 22, 255, 21, 29, 28, 255,
            23, 22, 21, 255, 22, 21, 28, 255, 31, 34, 39, 255, 37, 32, 37, 255,
            32, 31, 34, 255, 39, 37, 32, 255, 37, 32, 34, 255
        };

        surface_->SetSize(gfx::Size(INT_MAX, INT_MAX));

        ReadPixelsEmulator emu(
            kWidth, kHeight, kBytesPerPixel, kSrcPixels, kSrcPixels, kPackAlignment);
        typedef ReadPixels::Result Result;
        Result* result = GetSharedMemoryAs<Result*>();
        uint32 result_shm_id = kSharedMemoryId;
        uint32 result_shm_offset = kSharedMemoryOffset;
        uint32 pixels_shm_id = kSharedMemoryId;
        uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result);
        void* dest = &result[1];
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_,
            ReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, _))
            .WillOnce(Invoke(&emu, &ReadPixelsEmulator::ReadPixels));
        ReadPixels cmd;
        cmd.Init(0,
            0,
            kWidth,
            kHeight,
            GL_RGBA,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        for (GLint yy = 0; yy < kHeight; ++yy) {
            EXPECT_TRUE(emu.CompareRowSegment(
                0, yy, kWidth, emu.ComputePackAlignmentAddress(0, yy, kWidth, dest)));
        }
    }

    TEST_P(GLES2DecoderRGBBackbufferTest, ReadPixelsNoAlphaBackbuffer)
    {
        const GLsizei kWidth = 3;
        const GLsizei kHeight = 3;
        const GLint kBytesPerPixel = 4;
        const GLint kPackAlignment = 4;
        static const uint8 kExpectedPixels[kWidth * kHeight * kBytesPerPixel] = {
            12,
            13,
            14,
            255,
            19,
            18,
            19,
            255,
            13,
            14,
            18,
            255,
            29,
            28,
            23,
            255,
            21,
            22,
            21,
            255,
            28,
            23,
            22,
            255,
            31,
            34,
            39,
            255,
            32,
            37,
            32,
            255,
            34,
            39,
            37,
            255,
        };
        static const uint8 kSrcPixels[kWidth * kHeight * kBytesPerPixel] = {
            12,
            13,
            14,
            18,
            19,
            18,
            19,
            12,
            13,
            14,
            18,
            19,
            29,
            28,
            23,
            22,
            21,
            22,
            21,
            29,
            28,
            23,
            22,
            21,
            31,
            34,
            39,
            37,
            32,
            37,
            32,
            31,
            34,
            39,
            37,
            32,
        };

        surface_->SetSize(gfx::Size(INT_MAX, INT_MAX));

        ReadPixelsEmulator emu(kWidth,
            kHeight,
            kBytesPerPixel,
            kSrcPixels,
            kExpectedPixels,
            kPackAlignment);
        typedef ReadPixels::Result Result;
        Result* result = GetSharedMemoryAs<Result*>();
        uint32 result_shm_id = kSharedMemoryId;
        uint32 result_shm_offset = kSharedMemoryOffset;
        uint32 pixels_shm_id = kSharedMemoryId;
        uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(*result);
        void* dest = &result[1];
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_,
            ReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, _))
            .WillOnce(Invoke(&emu, &ReadPixelsEmulator::ReadPixels));
        ReadPixels cmd;
        cmd.Init(0,
            0,
            kWidth,
            kHeight,
            GL_RGBA,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        for (GLint yy = 0; yy < kHeight; ++yy) {
            EXPECT_TRUE(emu.CompareRowSegment(
                0, yy, kWidth, emu.ComputePackAlignmentAddress(0, yy, kWidth, dest)));
        }
    }

    TEST_P(GLES2DecoderTest, ReadPixelsOutOfRange)
    {
        static GLint tests[][4] = {
            {
                -2,
                -1,
                9,
                5,
            }, // out of range on all sides
            {
                2,
                1,
                9,
                5,
            }, // out of range on right, bottom
            {
                -7,
                -4,
                9,
                5,
            }, // out of range on left, top
            {
                0,
                -5,
                9,
                5,
            }, // completely off top
            {
                0,
                3,
                9,
                5,
            }, // completely off bottom
            {
                -9,
                0,
                9,
                5,
            }, // completely off left
            {
                5,
                0,
                9,
                5,
            }, // completely off right
        };

        for (size_t tt = 0; tt < arraysize(tests); ++tt) {
            CheckReadPixelsOutOfRange(
                tests[tt][0], tests[tt][1], tests[tt][2], tests[tt][3], tt == 0);
        }
    }

    TEST_P(GLES2DecoderTest, ReadPixelsInvalidArgs)
    {
        typedef ReadPixels::Result Result;
        uint32 result_shm_id = kSharedMemoryId;
        uint32 result_shm_offset = kSharedMemoryOffset;
        uint32 pixels_shm_id = kSharedMemoryId;
        uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(Result);
        EXPECT_CALL(*gl_, ReadPixels(_, _, _, _, _, _, _)).Times(0);
        ReadPixels cmd;
        cmd.Init(0,
            0,
            -1,
            1,
            GL_RGB,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
        cmd.Init(0,
            0,
            1,
            -1,
            GL_RGB,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
        cmd.Init(0,
            0,
            1,
            1,
            GL_RGB,
            GL_INT,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
        cmd.Init(0,
            0,
            1,
            1,
            GL_RGB,
            GL_UNSIGNED_BYTE,
            kInvalidSharedMemoryId,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
        cmd.Init(0,
            0,
            1,
            1,
            GL_RGB,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            kInvalidSharedMemoryOffset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
        cmd.Init(0,
            0,
            1,
            1,
            GL_RGB,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            kInvalidSharedMemoryId,
            result_shm_offset,
            false);
        EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
        cmd.Init(0,
            0,
            1,
            1,
            GL_RGB,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            kInvalidSharedMemoryOffset,
            false);
        EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
    }

    TEST_P(GLES2DecoderManualInitTest, ReadPixelsAsyncError)
    {
        InitState init;
        init.extensions = "GL_ARB_sync";
        init.gl_version = "opengl es 3.0";
        init.has_alpha = true;
        init.request_alpha = true;
        init.bind_generates_resource = true;
        InitDecoder(init);

        typedef ReadPixels::Result Result;

        const GLsizei kWidth = 4;
        const GLsizei kHeight = 4;
        uint32 result_shm_id = kSharedMemoryId;
        uint32 result_shm_offset = kSharedMemoryOffset;
        uint32 pixels_shm_id = kSharedMemoryId;
        uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(Result);

        EXPECT_CALL(*gl_, GetError())
            // first error check must pass to get to the test
            .WillOnce(Return(GL_NO_ERROR))
            // second check is after BufferData, simulate fail here
            .WillOnce(Return(GL_INVALID_OPERATION))
            // third error check is fall-through call to sync ReadPixels
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();

        EXPECT_CALL(*gl_,
            ReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, _))
            .Times(1);
        EXPECT_CALL(*gl_, GenBuffersARB(1, _)).Times(1);
        EXPECT_CALL(*gl_, DeleteBuffersARB(1, _)).Times(1);
        EXPECT_CALL(*gl_, BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, _)).Times(2);
        EXPECT_CALL(*gl_,
            BufferData(GL_PIXEL_PACK_BUFFER_ARB, _, NULL, GL_STREAM_READ))
            .Times(1);

        ReadPixels cmd;
        cmd.Init(0,
            0,
            kWidth,
            kHeight,
            GL_RGBA,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            true);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    // Check that if a renderbuffer is attached and GL returns
    // GL_FRAMEBUFFER_COMPLETE that the buffer is cleared and state is restored.
    TEST_P(GLES2DecoderTest, FramebufferRenderbufferClearColor)
    {
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        ClearColor color_cmd;
        ColorMask color_mask_cmd;
        Enable enable_cmd;
        FramebufferRenderbuffer cmd;
        color_cmd.Init(0.1f, 0.2f, 0.3f, 0.4f);
        color_mask_cmd.Init(0, 1, 0, 1);
        enable_cmd.Init(GL_SCISSOR_TEST);
        cmd.Init(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_RENDERBUFFER,
            client_renderbuffer_id_);
        InSequence sequence;
        EXPECT_CALL(*gl_, ClearColor(0.1f, 0.2f, 0.3f, 0.4f))
            .Times(1)
            .RetiresOnSaturation();
        SetupExpectationsForEnableDisable(GL_SCISSOR_TEST, true);
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_,
            FramebufferRenderbufferEXT(GL_FRAMEBUFFER,
                GL_COLOR_ATTACHMENT0,
                GL_RENDERBUFFER,
                kServiceRenderbufferId))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(color_cmd));
        EXPECT_EQ(error::kNoError, ExecuteCmd(color_mask_cmd));
        EXPECT_EQ(error::kNoError, ExecuteCmd(enable_cmd));
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    TEST_P(GLES2DecoderTest, FramebufferRenderbufferClearDepth)
    {
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        ClearDepthf depth_cmd;
        DepthMask depth_mask_cmd;
        FramebufferRenderbuffer cmd;
        depth_cmd.Init(0.5f);
        depth_mask_cmd.Init(false);
        cmd.Init(GL_FRAMEBUFFER,
            GL_DEPTH_ATTACHMENT,
            GL_RENDERBUFFER,
            client_renderbuffer_id_);
        InSequence sequence;
        EXPECT_CALL(*gl_, ClearDepth(0.5f)).Times(1).RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_,
            FramebufferRenderbufferEXT(GL_FRAMEBUFFER,
                GL_DEPTH_ATTACHMENT,
                GL_RENDERBUFFER,
                kServiceRenderbufferId))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(depth_cmd));
        EXPECT_EQ(error::kNoError, ExecuteCmd(depth_mask_cmd));
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    TEST_P(GLES2DecoderTest, FramebufferRenderbufferClearStencil)
    {
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        ClearStencil stencil_cmd;
        StencilMaskSeparate stencil_mask_separate_cmd;
        FramebufferRenderbuffer cmd;
        stencil_cmd.Init(123);
        stencil_mask_separate_cmd.Init(GL_BACK, 0x1234u);
        cmd.Init(GL_FRAMEBUFFER,
            GL_STENCIL_ATTACHMENT,
            GL_RENDERBUFFER,
            client_renderbuffer_id_);
        InSequence sequence;
        EXPECT_CALL(*gl_, ClearStencil(123)).Times(1).RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_,
            FramebufferRenderbufferEXT(GL_FRAMEBUFFER,
                GL_STENCIL_ATTACHMENT,
                GL_RENDERBUFFER,
                kServiceRenderbufferId))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(stencil_cmd));
        EXPECT_EQ(error::kNoError, ExecuteCmd(stencil_mask_separate_cmd));
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

#if 0 // Turn this test on once we allow GL_DEPTH_STENCIL_ATTACHMENT
TEST_P(GLES2DecoderTest, FramebufferRenderbufferClearDepthStencil) {
  DoBindFramebuffer(GL_FRAMEBUFFER, client_framebuffer_id_,
                    kServiceFramebufferId);
  ClearDepthf depth_cmd;
  ClearStencil stencil_cmd;
  FramebufferRenderbuffer cmd;
  depth_cmd.Init(0.5f);
  stencil_cmd.Init(123);
  cmd.Init(
      GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
      client_renderbuffer_id_);
  InSequence sequence;
  EXPECT_CALL(*gl_, ClearDepth(0.5f))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, ClearStencil(123))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(
      GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
      kServiceRenderbufferId))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_EQ(error::kNoError, ExecuteCmd(depth_cmd));
  EXPECT_EQ(error::kNoError, ExecuteCmd(stencil_cmd));
  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
}
#endif

    TEST_P(GLES2DecoderManualInitTest, ActualAlphaMatchesRequestedAlpha)
    {
        InitState init;
        init.has_alpha = true;
        init.request_alpha = true;
        init.bind_generates_resource = true;
        InitDecoder(init);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        result->size = 0;
        GetIntegerv cmd2;
        cmd2.Init(GL_ALPHA_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_ALPHA_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(8, result->GetData()[0]);
    }

    TEST_P(GLES2DecoderManualInitTest, ActualAlphaDoesNotMatchRequestedAlpha)
    {
        InitState init;
        init.has_alpha = true;
        init.bind_generates_resource = true;
        InitDecoder(init);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        result->size = 0;
        GetIntegerv cmd2;
        cmd2.Init(GL_ALPHA_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_ALPHA_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(0, result->GetData()[0]);
    }

    TEST_P(GLES2DecoderManualInitTest, ActualDepthMatchesRequestedDepth)
    {
        InitState init;
        init.has_depth = true;
        init.request_depth = true;
        init.bind_generates_resource = true;
        InitDecoder(init);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _))
            .WillOnce(SetArgPointee<1>(24))
            .RetiresOnSaturation();
        result->size = 0;
        GetIntegerv cmd2;
        cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(24, result->GetData()[0]);
    }

    TEST_P(GLES2DecoderManualInitTest, ActualDepthDoesNotMatchRequestedDepth)
    {
        InitState init;
        init.has_depth = true;
        init.bind_generates_resource = true;
        InitDecoder(init);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _))
            .WillOnce(SetArgPointee<1>(24))
            .RetiresOnSaturation();
        result->size = 0;
        GetIntegerv cmd2;
        cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(0, result->GetData()[0]);
    }

    TEST_P(GLES2DecoderManualInitTest, ActualStencilMatchesRequestedStencil)
    {
        InitState init;
        init.has_stencil = true;
        init.request_stencil = true;
        init.bind_generates_resource = true;
        InitDecoder(init);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _))
            .WillOnce(SetArgPointee<1>(8))
            .RetiresOnSaturation();
        result->size = 0;
        GetIntegerv cmd2;
        cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(8, result->GetData()[0]);
    }

    TEST_P(GLES2DecoderManualInitTest, ActualStencilDoesNotMatchRequestedStencil)
    {
        InitState init;
        init.has_stencil = true;
        init.bind_generates_resource = true;
        InitDecoder(init);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _))
            .WillOnce(SetArgPointee<1>(8))
            .RetiresOnSaturation();
        result->size = 0;
        GetIntegerv cmd2;
        cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(0, result->GetData()[0]);
    }

    TEST_P(GLES2DecoderManualInitTest, PackedDepthStencilReportsCorrectValues)
    {
        InitState init;
        init.extensions = "GL_OES_packed_depth_stencil";
        init.gl_version = "opengl es 2.0";
        init.has_depth = true;
        init.has_stencil = true;
        init.request_depth = true;
        init.request_stencil = true;
        init.bind_generates_resource = true;
        InitDecoder(init);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        result->size = 0;
        GetIntegerv cmd2;
        cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _))
            .WillOnce(SetArgPointee<1>(8))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(8, result->GetData()[0]);
        result->size = 0;
        cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _))
            .WillOnce(SetArgPointee<1>(24))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(24, result->GetData()[0]);
    }

    TEST_P(GLES2DecoderManualInitTest, PackedDepthStencilNoRequestedStencil)
    {
        InitState init;
        init.extensions = "GL_OES_packed_depth_stencil";
        init.gl_version = "opengl es 2.0";
        init.has_depth = true;
        init.has_stencil = true;
        init.request_depth = true;
        init.bind_generates_resource = true;
        InitDecoder(init);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        result->size = 0;
        GetIntegerv cmd2;
        cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _))
            .WillOnce(SetArgPointee<1>(8))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(0, result->GetData()[0]);
        result->size = 0;
        cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _))
            .WillOnce(SetArgPointee<1>(24))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(24, result->GetData()[0]);
    }

    TEST_P(GLES2DecoderManualInitTest, PackedDepthStencilRenderbufferDepth)
    {
        InitState init;
        init.extensions = "GL_OES_packed_depth_stencil";
        init.gl_version = "opengl es 2.0";
        init.bind_generates_resource = true;
        InitDecoder(init);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);

        EnsureRenderbufferBound(false);
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR)) // for RenderbufferStoage
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR)) // for FramebufferRenderbuffer
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR)) // for GetIntegerv
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR)) // for GetIntegerv
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();

        EXPECT_CALL(
            *gl_,
            RenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 100, 50))
            .Times(1)
            .RetiresOnSaturation();
        RenderbufferStorage cmd;
        cmd.Init(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 100, 50);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_CALL(*gl_,
            FramebufferRenderbufferEXT(GL_FRAMEBUFFER,
                GL_DEPTH_ATTACHMENT,
                GL_RENDERBUFFER,
                kServiceRenderbufferId))
            .Times(1)
            .RetiresOnSaturation();
        FramebufferRenderbuffer fbrb_cmd;
        fbrb_cmd.Init(GL_FRAMEBUFFER,
            GL_DEPTH_ATTACHMENT,
            GL_RENDERBUFFER,
            client_renderbuffer_id_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(fbrb_cmd));

        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        result->size = 0;
        GetIntegerv cmd2;
        cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _))
            .WillOnce(SetArgPointee<1>(8))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(0, result->GetData()[0]);
        result->size = 0;
        cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _))
            .WillOnce(SetArgPointee<1>(24))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(24, result->GetData()[0]);
    }

    TEST_P(GLES2DecoderManualInitTest, PackedDepthStencilRenderbufferStencil)
    {
        InitState init;
        init.extensions = "GL_OES_packed_depth_stencil";
        init.gl_version = "opengl es 2.0";
        init.bind_generates_resource = true;
        InitDecoder(init);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);

        EnsureRenderbufferBound(false);
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR)) // for RenderbufferStoage
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR)) // for FramebufferRenderbuffer
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR)) // for GetIntegerv
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR)) // for GetIntegerv
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();

        EXPECT_CALL(
            *gl_,
            RenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 100, 50))
            .Times(1)
            .RetiresOnSaturation();
        RenderbufferStorage cmd;
        cmd.Init(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 100, 50);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_CALL(*gl_,
            FramebufferRenderbufferEXT(GL_FRAMEBUFFER,
                GL_STENCIL_ATTACHMENT,
                GL_RENDERBUFFER,
                kServiceRenderbufferId))
            .Times(1)
            .RetiresOnSaturation();
        FramebufferRenderbuffer fbrb_cmd;
        fbrb_cmd.Init(GL_FRAMEBUFFER,
            GL_STENCIL_ATTACHMENT,
            GL_RENDERBUFFER,
            client_renderbuffer_id_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(fbrb_cmd));

        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        result->size = 0;
        GetIntegerv cmd2;
        cmd2.Init(GL_STENCIL_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _))
            .WillOnce(SetArgPointee<1>(8))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_STENCIL_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(8, result->GetData()[0]);
        result->size = 0;
        cmd2.Init(GL_DEPTH_BITS, shared_memory_id_, shared_memory_offset_);
        EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _))
            .WillOnce(SetArgPointee<1>(24))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        EXPECT_EQ(decoder_->GetGLES2Util()->GLGetNumValuesReturned(GL_DEPTH_BITS),
            result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_EQ(0, result->GetData()[0]);
    }

    TEST_P(GLES2DecoderTest, FramebufferRenderbufferGLError)
    {
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_OUT_OF_MEMORY))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_,
            FramebufferRenderbufferEXT(GL_FRAMEBUFFER,
                GL_COLOR_ATTACHMENT0,
                GL_RENDERBUFFER,
                kServiceRenderbufferId))
            .Times(1)
            .RetiresOnSaturation();
        FramebufferRenderbuffer cmd;
        cmd.Init(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_RENDERBUFFER,
            client_renderbuffer_id_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
    }

    TEST_P(GLES2DecoderTest, FramebufferTexture2DGLError)
    {
        const GLsizei kWidth = 5;
        const GLsizei kHeight = 3;
        const GLenum kFormat = GL_RGB;
        DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
        DoTexImage2D(GL_TEXTURE_2D,
            0,
            kFormat,
            kWidth,
            kHeight,
            0,
            kFormat,
            GL_UNSIGNED_BYTE,
            0,
            0);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_OUT_OF_MEMORY))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_,
            FramebufferTexture2DEXT(GL_FRAMEBUFFER,
                GL_COLOR_ATTACHMENT0,
                GL_TEXTURE_2D,
                kServiceTextureId,
                0))
            .Times(1)
            .RetiresOnSaturation();
        FramebufferTexture2D fbtex_cmd;
        fbtex_cmd.Init(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            client_texture_id_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(fbtex_cmd));
        EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
    }

    TEST_P(GLES2DecoderTest, RenderbufferStorageGLError)
    {
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        EnsureRenderbufferBound(false);
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_OUT_OF_MEMORY))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, RenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, 100, 50))
            .Times(1)
            .RetiresOnSaturation();
        RenderbufferStorage cmd;
        cmd.Init(GL_RENDERBUFFER, GL_RGBA4, 100, 50);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
    }

    TEST_P(GLES2DecoderTest, RenderbufferStorageBadArgs)
    {
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        EXPECT_CALL(*gl_, RenderbufferStorageEXT(_, _, _, _))
            .Times(0)
            .RetiresOnSaturation();
        RenderbufferStorage cmd;
        cmd.Init(GL_RENDERBUFFER, GL_RGBA4, TestHelper::kMaxRenderbufferSize + 1, 1);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
        cmd.Init(GL_RENDERBUFFER, GL_RGBA4, 1, TestHelper::kMaxRenderbufferSize + 1);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
    }

    TEST_P(GLES3DecoderTest, ClearBufferivImmediateValidArgs)
    {
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        DoRenderbufferStorage(
            GL_RENDERBUFFER, GL_RGBA8I, GL_RGBA8I, 1, 1, GL_NO_ERROR);
        DoFramebufferRenderbuffer(
            GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
            client_renderbuffer_id_, kServiceRenderbufferId, GL_NO_ERROR);

        cmds::ClearBufferivImmediate& cmd = *GetImmediateAs<cmds::ClearBufferivImmediate>();
        GLint temp[4] = { 0 };
        cmd.Init(GL_COLOR, 0, &temp[0]);
        EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER))
            .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
            .RetiresOnSaturation();
        SetupExpectationsForApplyingDirtyState(
            false, false, false, 0x1111, false, false, 0, 0, false);
        EXPECT_CALL(*gl_, ClearBufferiv(GL_COLOR, 0, reinterpret_cast<GLint*>(ImmediateDataAddress(&cmd))));
        EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES3DecoderTest, ClearBufferuivImmediateValidArgs)
    {
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        DoRenderbufferStorage(
            GL_RENDERBUFFER, GL_RGBA8UI, GL_RGBA8UI, 1, 1, GL_NO_ERROR);
        DoFramebufferRenderbuffer(
            GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
            client_renderbuffer_id_, kServiceRenderbufferId, GL_NO_ERROR);

        cmds::ClearBufferuivImmediate& cmd = *GetImmediateAs<cmds::ClearBufferuivImmediate>();
        GLuint temp[4] = { 0u };
        cmd.Init(GL_COLOR, 0, &temp[0]);
        EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER))
            .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
            .RetiresOnSaturation();
        SetupExpectationsForApplyingDirtyState(
            false, false, false, 0x1111, false, false, 0, 0, false);
        EXPECT_CALL(*gl_, ClearBufferuiv(GL_COLOR, 0, reinterpret_cast<GLuint*>(ImmediateDataAddress(&cmd))));
        EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES3DecoderTest, ClearBufferfvImmediateValidArgs)
    {
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        DoRenderbufferStorage(
            GL_RENDERBUFFER, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT32F,
            1, 1, GL_NO_ERROR);
        DoFramebufferRenderbuffer(
            GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
            client_renderbuffer_id_, kServiceRenderbufferId, GL_NO_ERROR);

        Enable cmd_enable;
        cmd_enable.Init(GL_DEPTH_TEST);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd_enable));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());

        cmds::ClearBufferfvImmediate& cmd = *GetImmediateAs<cmds::ClearBufferfvImmediate>();
        GLfloat temp[4] = { 1.0f };
        cmd.Init(GL_DEPTH, 0, &temp[0]);
        EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER))
            .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
            .RetiresOnSaturation();
        SetupExpectationsForApplyingDirtyState(
            true, true, false, 0x1110, true, true, 0, 0, false);
        EXPECT_CALL(*gl_, ClearBufferfv(GL_DEPTH, 0, reinterpret_cast<GLfloat*>(ImmediateDataAddress(&cmd))));
        EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES3DecoderTest, ClearBufferfiValidArgs)
    {
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        DoRenderbufferStorage(
            GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, GL_DEPTH24_STENCIL8,
            1, 1, GL_NO_ERROR);
        DoFramebufferRenderbuffer(
            GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
            client_renderbuffer_id_, kServiceRenderbufferId, GL_NO_ERROR);

        Enable cmd_enable;
        cmd_enable.Init(GL_STENCIL_TEST);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd_enable));
        cmd_enable.Init(GL_DEPTH_TEST);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd_enable));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());

        cmds::ClearBufferfi cmd;
        cmd.Init(GL_DEPTH_STENCIL, 0, 1.0f, 0);
        EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER))
            .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
            .RetiresOnSaturation();
        SetupExpectationsForApplyingDirtyState(
            true, true, true, 0x1110, true, true,
            GLES2Decoder::kDefaultStencilMask, GLES2Decoder::kDefaultStencilMask,
            true);
        EXPECT_CALL(*gl_, ClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0));
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES2DecoderManualInitTest,
        RenderbufferStorageMultisampleCHROMIUMGLError)
    {
        InitState init;
        init.extensions = "GL_EXT_framebuffer_multisample";
        init.bind_generates_resource = true;
        InitDecoder(init);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        EnsureRenderbufferBound(false);
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_OUT_OF_MEMORY))
            .RetiresOnSaturation();
        EXPECT_CALL(
            *gl_,
            RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 1, GL_RGBA, 100, 50))
            .Times(1)
            .RetiresOnSaturation();
        RenderbufferStorageMultisampleCHROMIUM cmd;
        cmd.Init(GL_RENDERBUFFER, 1, GL_RGBA4, 100, 50);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
    }

    TEST_P(GLES2DecoderManualInitTest,
        RenderbufferStorageMultisampleCHROMIUMBadArgs)
    {
        InitState init;
        init.extensions = "GL_EXT_framebuffer_multisample";
        init.bind_generates_resource = true;
        InitDecoder(init);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        EXPECT_CALL(*gl_, RenderbufferStorageMultisampleEXT(_, _, _, _, _))
            .Times(0)
            .RetiresOnSaturation();
        RenderbufferStorageMultisampleCHROMIUM cmd;
        cmd.Init(GL_RENDERBUFFER,
            TestHelper::kMaxSamples + 1,
            GL_RGBA4,
            TestHelper::kMaxRenderbufferSize,
            1);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
        cmd.Init(GL_RENDERBUFFER,
            TestHelper::kMaxSamples,
            GL_RGBA4,
            TestHelper::kMaxRenderbufferSize + 1,
            1);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
        cmd.Init(GL_RENDERBUFFER,
            TestHelper::kMaxSamples,
            GL_RGBA4,
            1,
            TestHelper::kMaxRenderbufferSize + 1);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
    }

    TEST_P(GLES2DecoderManualInitTest, RenderbufferStorageMultisampleCHROMIUM)
    {
        InitState init;
        init.extensions = "GL_EXT_framebuffer_multisample";
        InitDecoder(init);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        InSequence sequence;
        EnsureRenderbufferBound(false);
        DoRenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER,
            TestHelper::kMaxSamples,
            GL_RGBA4,
            GL_RGBA,
            TestHelper::kMaxRenderbufferSize,
            1);
    }

    TEST_P(GLES2DecoderManualInitTest,
        RenderbufferStorageMultisampleCHROMIUMRebindRenderbuffer)
    {
        InitState init;
        init.extensions = "GL_EXT_framebuffer_multisample";
        InitDecoder(init);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        RestoreRenderbufferBindings();
        InSequence sequence;
        EnsureRenderbufferBound(true);
        DoRenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER,
            TestHelper::kMaxSamples,
            GL_RGBA4,
            GL_RGBA,
            TestHelper::kMaxRenderbufferSize,
            1);
    }

    TEST_P(GLES2DecoderManualInitTest,
        RenderbufferStorageMultisampleEXTNotSupported)
    {
        InitState init;
        init.extensions = "GL_EXT_framebuffer_multisample";
        init.bind_generates_resource = true;
        InitDecoder(init);
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        InSequence sequence;
        // GL_EXT_framebuffer_multisample uses RenderbufferStorageMultisampleCHROMIUM.
        RenderbufferStorageMultisampleEXT cmd;
        cmd.Init(GL_RENDERBUFFER,
            TestHelper::kMaxSamples,
            GL_RGBA4,
            TestHelper::kMaxRenderbufferSize,
            1);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
    }

    class GLES2DecoderMultisampledRenderToTextureTest
        : public GLES2DecoderTestWithExtensionsOnGLES2 {
    public:
        void TestNotCompatibleWithRenderbufferStorageMultisampleCHROMIUM()
        {
            DoBindRenderbuffer(
                GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
            RenderbufferStorageMultisampleCHROMIUM cmd;
            cmd.Init(GL_RENDERBUFFER,
                TestHelper::kMaxSamples,
                GL_RGBA4,
                TestHelper::kMaxRenderbufferSize,
                1);
            EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
            EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
        }

        void TestRenderbufferStorageMultisampleEXT(const char* extension,
            bool rb_rebind)
        {
            DoBindRenderbuffer(
                GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
            InSequence sequence;
            if (rb_rebind) {
                RestoreRenderbufferBindings();
                EnsureRenderbufferBound(true);
            } else {
                EnsureRenderbufferBound(false);
            }

            EXPECT_CALL(*gl_, GetError())
                .WillOnce(Return(GL_NO_ERROR))
                .RetiresOnSaturation();
            if (strstr(extension, "GL_IMG_multisampled_render_to_texture")) {
                EXPECT_CALL(
                    *gl_,
                    RenderbufferStorageMultisampleIMG(GL_RENDERBUFFER,
                        TestHelper::kMaxSamples,
                        GL_RGBA4,
                        TestHelper::kMaxRenderbufferSize,
                        1))
                    .Times(1)
                    .RetiresOnSaturation();
            } else {
                EXPECT_CALL(
                    *gl_,
                    RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER,
                        TestHelper::kMaxSamples,
                        GL_RGBA4,
                        TestHelper::kMaxRenderbufferSize,
                        1))
                    .Times(1)
                    .RetiresOnSaturation();
            }
            EXPECT_CALL(*gl_, GetError())
                .WillOnce(Return(GL_NO_ERROR))
                .RetiresOnSaturation();
            RenderbufferStorageMultisampleEXT cmd;
            cmd.Init(GL_RENDERBUFFER,
                TestHelper::kMaxSamples,
                GL_RGBA4,
                TestHelper::kMaxRenderbufferSize,
                1);
            EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
            EXPECT_EQ(GL_NO_ERROR, GetGLError());
        }
    };

    INSTANTIATE_TEST_CASE_P(Service,
        GLES2DecoderMultisampledRenderToTextureTest,
        ::testing::Bool());

    TEST_P(GLES2DecoderMultisampledRenderToTextureTest,
        NotCompatibleWithRenderbufferStorageMultisampleCHROMIUM_EXT)
    {
        Init("GL_EXT_multisampled_render_to_texture");
        TestNotCompatibleWithRenderbufferStorageMultisampleCHROMIUM();
    }

    TEST_P(GLES2DecoderMultisampledRenderToTextureTest,
        NotCompatibleWithRenderbufferStorageMultisampleCHROMIUM_IMG)
    {
        Init("GL_IMG_multisampled_render_to_texture");
        TestNotCompatibleWithRenderbufferStorageMultisampleCHROMIUM();
    }

    TEST_P(GLES2DecoderMultisampledRenderToTextureTest,
        RenderbufferStorageMultisampleEXT_EXT)
    {
        Init("GL_EXT_multisampled_render_to_texture");
        TestRenderbufferStorageMultisampleEXT("GL_EXT_multisampled_render_to_texture",
            false);
    }

    TEST_P(GLES2DecoderMultisampledRenderToTextureTest,
        RenderbufferStorageMultisampleEXT_IMG)
    {
        Init("GL_IMG_multisampled_render_to_texture");
        TestRenderbufferStorageMultisampleEXT("GL_IMG_multisampled_render_to_texture",
            false);
    }

    TEST_P(GLES2DecoderMultisampledRenderToTextureTest,
        RenderbufferStorageMultisampleEXT_EXT_RebindRenderbuffer)
    {
        Init("GL_EXT_multisampled_render_to_texture");
        TestRenderbufferStorageMultisampleEXT("GL_EXT_multisampled_render_to_texture",
            true);
    }

    TEST_P(GLES2DecoderMultisampledRenderToTextureTest,
        RenderbufferStorageMultisampleEXT_IMG_RebindRenderbuffer)
    {
        Init("GL_IMG_multisampled_render_to_texture");
        TestRenderbufferStorageMultisampleEXT("GL_IMG_multisampled_render_to_texture",
            true);
    }

    TEST_P(GLES2DecoderTest, ReadPixelsGLError)
    {
        GLenum kFormat = GL_RGBA;
        GLint x = 0;
        GLint y = 0;
        GLsizei width = 2;
        GLsizei height = 4;
        typedef ReadPixels::Result Result;
        uint32 result_shm_id = kSharedMemoryId;
        uint32 result_shm_offset = kSharedMemoryOffset;
        uint32 pixels_shm_id = kSharedMemoryId;
        uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(Result);
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_OUT_OF_MEMORY))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_,
            ReadPixels(x, y, width, height, kFormat, GL_UNSIGNED_BYTE, _))
            .Times(1)
            .RetiresOnSaturation();
        ReadPixels cmd;
        cmd.Init(x,
            y,
            width,
            height,
            kFormat,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_OUT_OF_MEMORY, GetGLError());
    }

    TEST_P(GLES2DecoderWithShaderTest, UnClearedAttachmentsGetClearedOnClear)
    {
        const GLuint kFBOClientTextureId = 4100;
        const GLuint kFBOServiceTextureId = 4101;

        // Register a texture id.
        EXPECT_CALL(*gl_, GenTextures(_, _))
            .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
            .RetiresOnSaturation();
        GenHelper<GenTexturesImmediate>(kFBOClientTextureId);

        // Setup "render to" texture.
        DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
        DoTexImage2D(
            GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoFramebufferTexture2D(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            kFBOClientTextureId,
            kFBOServiceTextureId,
            0,
            GL_NO_ERROR);
        // Set scissor rect and enable GL_SCISSOR_TEST to make sure we re-enable it
        // and restore the rect again after the clear.
        DoEnableDisable(GL_SCISSOR_TEST, true);
        DoScissor(0, 0, 64, 64);

        // Setup "render from" texture.
        SetupTexture();

        SetupExpectationsForFramebufferClearing(GL_FRAMEBUFFER, // target
            GL_COLOR_BUFFER_BIT, // clear bits
            0, 0, 0,
            0, // color
            0, // stencil
            1.0f, // depth
            true, // scissor test
            0, 0, 64, 64);
        SetupExpectationsForApplyingDirtyState(false, // Framebuffer is RGB
            false, // Framebuffer has depth
            false, // Framebuffer has stencil
            0x1111, // color bits
            false, // depth mask
            false, // depth enabled
            0, // front stencil mask
            0, // back stencil mask
            false); // stencil enabled

        EXPECT_CALL(*gl_, Clear(GL_COLOR_BUFFER_BIT)).Times(1).RetiresOnSaturation();

        Clear cmd;
        cmd.Init(GL_COLOR_BUFFER_BIT);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES2DecoderWithShaderTest, UnClearedAttachmentsGetClearedOnReadPixels)
    {
        const GLuint kFBOClientTextureId = 4100;
        const GLuint kFBOServiceTextureId = 4101;

        // Register a texture id.
        EXPECT_CALL(*gl_, GenTextures(_, _))
            .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
            .RetiresOnSaturation();
        GenHelper<GenTexturesImmediate>(kFBOClientTextureId);

        // Setup "render to" texture.
        DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
        DoTexImage2D(
            GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoFramebufferTexture2D(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            kFBOClientTextureId,
            kFBOServiceTextureId,
            0,
            GL_NO_ERROR);
        DoEnableDisable(GL_SCISSOR_TEST, false);
        DoScissor(0, 0, 1, 1);

        // Setup "render from" texture.
        SetupTexture();

        SetupExpectationsForFramebufferClearing(GL_FRAMEBUFFER, // target
            GL_COLOR_BUFFER_BIT, // clear bits
            0, 0, 0,
            0, // color
            0, // stencil
            1.0f, // depth
            false, // scissor test
            0, 0, 1, 1);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, ReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, _))
            .Times(1)
            .RetiresOnSaturation();
        typedef ReadPixels::Result Result;
        uint32 result_shm_id = kSharedMemoryId;
        uint32 result_shm_offset = kSharedMemoryOffset;
        uint32 pixels_shm_id = kSharedMemoryId;
        uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(Result);
        ReadPixels cmd;
        cmd.Init(0,
            0,
            1,
            1,
            GL_RGBA,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES2DecoderManualInitTest,
        UnClearedAttachmentsGetClearedOnReadPixelsAndDrawBufferGetsRestored)
    {
        InitState init;
        init.extensions = "GL_EXT_framebuffer_multisample";
        init.bind_generates_resource = true;
        InitDecoder(init);
        const GLuint kFBOClientTextureId = 4100;
        const GLuint kFBOServiceTextureId = 4101;

        // Register a texture id.
        EXPECT_CALL(*gl_, GenTextures(_, _))
            .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
            .RetiresOnSaturation();
        GenHelper<GenTexturesImmediate>(kFBOClientTextureId);

        // Setup "render from" texture.
        DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
        DoTexImage2D(
            GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
        DoBindFramebuffer(
            GL_READ_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoFramebufferTexture2D(GL_READ_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            kFBOClientTextureId,
            kFBOServiceTextureId,
            0,
            GL_NO_ERROR);

        // Set scissor rect and disable GL_SCISSOR_TEST to make sure we enable it in
        // the clear, then disable it and restore the rect again.
        DoScissor(0, 0, 32, 32);
        DoEnableDisable(GL_SCISSOR_TEST, false);

        SetupExpectationsForFramebufferClearingMulti(
            kServiceFramebufferId, // read framebuffer service id
            0, // backbuffer service id
            GL_READ_FRAMEBUFFER, // target
            GL_COLOR_BUFFER_BIT, // clear bits
            0, 0, 0,
            0, // color
            0, // stencil
            1.0f, // depth
            false, // scissor test
            0, 0, 32, 32);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, ReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, _))
            .Times(1)
            .RetiresOnSaturation();
        typedef ReadPixels::Result Result;
        uint32 result_shm_id = kSharedMemoryId;
        uint32 result_shm_offset = kSharedMemoryOffset;
        uint32 pixels_shm_id = kSharedMemoryId;
        uint32 pixels_shm_offset = kSharedMemoryOffset + sizeof(Result);
        ReadPixels cmd;
        cmd.Init(0,
            0,
            1,
            1,
            GL_RGBA,
            GL_UNSIGNED_BYTE,
            pixels_shm_id,
            pixels_shm_offset,
            result_shm_id,
            result_shm_offset,
            false);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES2DecoderWithShaderTest, CopyTexImageWithInCompleteFBOFails)
    {
        GLenum target = GL_TEXTURE_2D;
        GLint level = 0;
        GLenum internal_format = GL_RGBA;
        GLsizei width = 2;
        GLsizei height = 4;
        SetupTexture();
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, GL_RGBA, 0, 0, GL_NO_ERROR);
        DoFramebufferRenderbuffer(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_RENDERBUFFER,
            client_renderbuffer_id_,
            kServiceRenderbufferId,
            GL_NO_ERROR);

        EXPECT_CALL(*gl_, CopyTexImage2D(_, _, _, _, _, _, _, _))
            .Times(0)
            .RetiresOnSaturation();
        CopyTexImage2D cmd;
        cmd.Init(target, level, internal_format, 0, 0, width, height);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_INVALID_FRAMEBUFFER_OPERATION, GetGLError());
    }

    void GLES2DecoderWithShaderTest::CheckRenderbufferChangesMarkFBOAsNotComplete(
        bool bound_fbo)
    {
        FramebufferManager* framebuffer_manager = group().framebuffer_manager();
        SetupTexture();
        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, GL_RGBA, 1, 1, GL_NO_ERROR);
        DoFramebufferRenderbuffer(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_RENDERBUFFER,
            client_renderbuffer_id_,
            kServiceRenderbufferId,
            GL_NO_ERROR);

        if (!bound_fbo) {
            DoBindFramebuffer(GL_FRAMEBUFFER, 0, 0);
        }

        Framebuffer* framebuffer = framebuffer_manager->GetFramebuffer(client_framebuffer_id_);
        ASSERT_TRUE(framebuffer != NULL);
        framebuffer_manager->MarkAsComplete(framebuffer);
        EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer));

        // Test that renderbufferStorage marks fbo as not complete.
        DoRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, GL_RGBA, 1, 1, GL_NO_ERROR);
        EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer));
        framebuffer_manager->MarkAsComplete(framebuffer);
        EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer));

        // Test deleting renderbuffer marks fbo as not complete.
        DoDeleteRenderbuffer(client_renderbuffer_id_, kServiceRenderbufferId);
        if (bound_fbo) {
            EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer));
        } else {
            EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer));
        }
        // Cleanup
        DoDeleteFramebuffer(client_framebuffer_id_,
            kServiceFramebufferId,
            bound_fbo,
            GL_FRAMEBUFFER,
            0,
            bound_fbo,
            GL_FRAMEBUFFER,
            0);
    }

    TEST_P(GLES2DecoderWithShaderTest,
        RenderbufferChangesMarkFBOAsNotCompleteBoundFBO)
    {
        CheckRenderbufferChangesMarkFBOAsNotComplete(true);
    }

    TEST_P(GLES2DecoderWithShaderTest,
        RenderbufferChangesMarkFBOAsNotCompleteUnboundFBO)
    {
        CheckRenderbufferChangesMarkFBOAsNotComplete(false);
    }

    void GLES2DecoderWithShaderTest::CheckTextureChangesMarkFBOAsNotComplete(
        bool bound_fbo)
    {
        FramebufferManager* framebuffer_manager = group().framebuffer_manager();
        const GLuint kFBOClientTextureId = 4100;
        const GLuint kFBOServiceTextureId = 4101;

        // Register a texture id.
        EXPECT_CALL(*gl_, GenTextures(_, _))
            .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
            .RetiresOnSaturation();
        GenHelper<GenTexturesImmediate>(kFBOClientTextureId);

        SetupTexture();

        // Setup "render to" texture.
        DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
        DoTexImage2D(
            GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoFramebufferTexture2D(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            kFBOClientTextureId,
            kFBOServiceTextureId,
            0,
            GL_NO_ERROR);

        DoBindRenderbuffer(
            GL_RENDERBUFFER, client_renderbuffer_id_, kServiceRenderbufferId);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoRenderbufferStorage(GL_RENDERBUFFER,
            GL_DEPTH_COMPONENT16,
            GL_DEPTH_COMPONENT,
            1,
            1,
            GL_NO_ERROR);
        DoFramebufferRenderbuffer(GL_FRAMEBUFFER,
            GL_DEPTH_ATTACHMENT,
            GL_RENDERBUFFER,
            client_renderbuffer_id_,
            kServiceRenderbufferId,
            GL_NO_ERROR);

        if (!bound_fbo) {
            DoBindFramebuffer(GL_FRAMEBUFFER, 0, 0);
        }

        Framebuffer* framebuffer = framebuffer_manager->GetFramebuffer(client_framebuffer_id_);
        ASSERT_TRUE(framebuffer != NULL);
        framebuffer_manager->MarkAsComplete(framebuffer);
        EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer));

        // Test TexImage2D marks fbo as not complete.
        DoTexImage2D(
            GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, 0, 0);
        EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer));
        framebuffer_manager->MarkAsComplete(framebuffer);
        EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer));

        // Test CopyImage2D marks fbo as not complete.
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, CopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 1, 1, 0))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        CopyTexImage2D cmd;
        cmd.Init(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 1, 1);
        // Unbind fbo and bind again after CopyTexImage2D tp avoid feedback loops.
        if (bound_fbo) {
            DoBindFramebuffer(GL_FRAMEBUFFER, 0, 0);
        }
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        if (bound_fbo) {
            DoBindFramebuffer(
                GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        }
        EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer));

        // Test deleting texture marks fbo as not complete.
        framebuffer_manager->MarkAsComplete(framebuffer);
        EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer));
        DoDeleteTexture(kFBOClientTextureId, kFBOServiceTextureId);

        if (bound_fbo) {
            EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer));
        } else {
            EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer));
        }
        // Cleanup
        DoDeleteFramebuffer(client_framebuffer_id_,
            kServiceFramebufferId,
            bound_fbo,
            GL_FRAMEBUFFER,
            0,
            bound_fbo,
            GL_FRAMEBUFFER,
            0);
    }

    TEST_P(GLES2DecoderWithShaderTest, TextureChangesMarkFBOAsNotCompleteBoundFBO)
    {
        CheckTextureChangesMarkFBOAsNotComplete(true);
    }

    TEST_P(GLES2DecoderWithShaderTest,
        TextureChangesMarkFBOAsNotCompleteUnboundFBO)
    {
        CheckTextureChangesMarkFBOAsNotComplete(false);
    }

    TEST_P(GLES2DecoderTest, CanChangeSurface)
    {
        scoped_refptr<GLSurfaceMock> other_surface(new GLSurfaceMock);
        EXPECT_CALL(*other_surface.get(), GetBackingFrameBufferObject())
            .WillOnce(Return(7));
        EXPECT_CALL(*gl_, BindFramebufferEXT(GL_FRAMEBUFFER_EXT, 7));

        decoder_->SetSurface(other_surface);
    }

    TEST_P(GLES2DecoderTest, DrawBuffersEXTImmediateSuccceeds)
    {
        const GLsizei count = 1;
        const GLenum bufs[] = { GL_COLOR_ATTACHMENT0 };
        DrawBuffersEXTImmediate& cmd = *GetImmediateAs<DrawBuffersEXTImmediate>();
        cmd.Init(count, bufs);

        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        EXPECT_CALL(*gl_, DrawBuffersARB(count, _)).Times(1).RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(bufs)));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES2DecoderTest, DrawBuffersEXTImmediateFails)
    {
        const GLsizei count = 1;
        const GLenum bufs[] = { GL_COLOR_ATTACHMENT1_EXT };
        DrawBuffersEXTImmediate& cmd = *GetImmediateAs<DrawBuffersEXTImmediate>();
        cmd.Init(count, bufs);

        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(bufs)));
        EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
    }

    TEST_P(GLES2DecoderTest, DrawBuffersEXTImmediateBackbuffer)
    {
        const GLsizei count = 1;
        const GLenum bufs[] = { GL_BACK };
        DrawBuffersEXTImmediate& cmd = *GetImmediateAs<DrawBuffersEXTImmediate>();
        cmd.Init(count, bufs);

        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(bufs)));
        EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());

        DoBindFramebuffer(GL_FRAMEBUFFER, 0, 0); // unbind

        EXPECT_CALL(*gl_, DrawBuffersARB(count, _)).Times(1).RetiresOnSaturation();

        EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(bufs)));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES2DecoderManualInitTest, InvalidateFramebufferBinding)
    {
        InitState init;
        init.gl_version = "opengl es 3.0";
        InitDecoder(init);

        // EXPECT_EQ can't be used to compare function pointers
        EXPECT_TRUE(
            gfx::MockGLInterface::GetGLProcAddress("glInvalidateFramebuffer") != gfx::g_driver_gl.fn.glDiscardFramebufferEXTFn);
        EXPECT_TRUE(
            gfx::MockGLInterface::GetGLProcAddress("glInvalidateFramebuffer") != gfx::MockGLInterface::GetGLProcAddress("glDiscardFramebufferEXT"));
    }

    TEST_P(GLES2DecoderManualInitTest, DiscardFramebufferEXT)
    {
        InitState init;
        init.extensions = "GL_EXT_discard_framebuffer";
        init.gl_version = "opengl es 2.0";
        InitDecoder(init);

        // EXPECT_EQ can't be used to compare function pointers
        EXPECT_TRUE(
            gfx::MockGLInterface::GetGLProcAddress("glDiscardFramebufferEXT") == gfx::g_driver_gl.fn.glDiscardFramebufferEXTFn);

        const GLenum target = GL_FRAMEBUFFER;
        const GLsizei count = 1;
        const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };

        SetupTexture();
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoFramebufferTexture2D(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            client_texture_id_,
            kServiceTextureId,
            0,
            GL_NO_ERROR);
        FramebufferManager* framebuffer_manager = group().framebuffer_manager();
        Framebuffer* framebuffer = framebuffer_manager->GetFramebuffer(client_framebuffer_id_);
        EXPECT_TRUE(framebuffer->IsCleared());

        EXPECT_CALL(*gl_, DiscardFramebufferEXT(target, count, _))
            .Times(1)
            .RetiresOnSaturation();
        DiscardFramebufferEXTImmediate& cmd = *GetImmediateAs<DiscardFramebufferEXTImmediate>();
        cmd.Init(target, count, attachments);

        EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(attachments)));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_FALSE(framebuffer->IsCleared());
    }

    TEST_P(GLES2DecoderTest, DiscardFramebufferEXTUnsupported)
    {
        const GLenum target = GL_FRAMEBUFFER;
        const GLsizei count = 1;
        const GLenum attachments[] = { GL_COLOR_EXT };
        DiscardFramebufferEXTImmediate& cmd = *GetImmediateAs<DiscardFramebufferEXTImmediate>();
        cmd.Init(target, count, attachments);

        // Should not result into a call into GL.
        EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(attachments)));
        EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
    }

    TEST_P(GLES2DecoderManualInitTest,
        DiscardedAttachmentsEXTMarksFramebufferIncomplete)
    {
        InitState init;
        init.extensions = "GL_EXT_discard_framebuffer";
        init.gl_version = "opengl es 2.0";
        init.has_alpha = true;
        init.bind_generates_resource = true;
        InitDecoder(init);

        const GLuint kFBOClientTextureId = 4100;
        const GLuint kFBOServiceTextureId = 4101;

        // Register a texture id.
        EXPECT_CALL(*gl_, GenTextures(_, _))
            .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
            .RetiresOnSaturation();
        GenHelper<GenTexturesImmediate>(kFBOClientTextureId);

        // Setup "render to" texture.
        DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
        DoTexImage2D(
            GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoFramebufferTexture2D(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            kFBOClientTextureId,
            kFBOServiceTextureId,
            0,
            GL_NO_ERROR);
        DoEnableDisable(GL_SCISSOR_TEST, false);
        DoScissor(0, 0, 1, 1);

        // Setup "render from" texture.
        SetupTexture();

        SetupExpectationsForFramebufferClearing(GL_FRAMEBUFFER, // target
            GL_COLOR_BUFFER_BIT, // clear bits
            0, 0, 0,
            0, // color
            0, // stencil
            1.0f, // depth
            false, // scissor test
            0, 0, 1, 1);
        SetupExpectationsForApplyingDirtyState(false, // Framebuffer is RGB
            false, // Framebuffer has depth
            false, // Framebuffer has stencil
            0x1111, // color bits
            false, // depth mask
            false, // depth enabled
            0, // front stencil mask
            0, // back stencil mask
            false); // stencil enabled

        EXPECT_CALL(*gl_, Clear(GL_COLOR_BUFFER_BIT)).Times(1).RetiresOnSaturation();

        Clear clear_cmd;
        clear_cmd.Init(GL_COLOR_BUFFER_BIT);
        EXPECT_EQ(error::kNoError, ExecuteCmd(clear_cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());

        // Check that framebuffer is cleared and complete.
        FramebufferManager* framebuffer_manager = group().framebuffer_manager();
        Framebuffer* framebuffer = framebuffer_manager->GetFramebuffer(client_framebuffer_id_);
        EXPECT_TRUE(framebuffer->IsCleared());
        EXPECT_TRUE(framebuffer_manager->IsComplete(framebuffer));

        // Check that Discard GL_COLOR_ATTACHMENT0, sets the attachment as uncleared
        // and the framebuffer as incomplete.
        EXPECT_TRUE(
            gfx::MockGLInterface::GetGLProcAddress("glDiscardFramebufferEXT") == gfx::g_driver_gl.fn.glDiscardFramebufferEXTFn);

        const GLenum target = GL_FRAMEBUFFER;
        const GLsizei count = 1;
        const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };

        DiscardFramebufferEXTImmediate& discard_cmd = *GetImmediateAs<DiscardFramebufferEXTImmediate>();
        discard_cmd.Init(target, count, attachments);

        EXPECT_CALL(*gl_, DiscardFramebufferEXT(target, count, _))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError,
            ExecuteImmediateCmd(discard_cmd, sizeof(attachments)));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
        EXPECT_FALSE(framebuffer->IsCleared());
        EXPECT_FALSE(framebuffer_manager->IsComplete(framebuffer));
    }

    TEST_P(GLES2DecoderManualInitTest, ReadFormatExtension)
    {
        InitState init;
        init.extensions = "GL_OES_read_format";
        init.bind_generates_resource = true;
        InitDecoder(init);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError()).Times(6).RetiresOnSaturation();

        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        GetIntegerv cmd;
        const GLuint kFBOClientTextureId = 4100;
        const GLuint kFBOServiceTextureId = 4101;

        // Register a texture id.
        EXPECT_CALL(*gl_, GenTextures(_, _))
            .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
            .RetiresOnSaturation();
        GenHelper<GenTexturesImmediate>(kFBOClientTextureId);

        // Setup "render to" texture.
        DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
        DoTexImage2D(
            GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoFramebufferTexture2D(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            kFBOClientTextureId,
            kFBOServiceTextureId,
            0,
            GL_NO_ERROR);

        result->size = 0;
        EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(1).RetiresOnSaturation();
        cmd.Init(GL_IMPLEMENTATION_COLOR_READ_FORMAT,
            shared_memory_id_,
            shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(1, result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());

        result->size = 0;
        EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(1).RetiresOnSaturation();
        cmd.Init(GL_IMPLEMENTATION_COLOR_READ_TYPE,
            shared_memory_id_,
            shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(1, result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    TEST_P(GLES2DecoderManualInitTest, NoReadFormatExtension)
    {
        InitState init;
        init.bind_generates_resource = true;
        InitDecoder(init);

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();

        typedef GetIntegerv::Result Result;
        Result* result = static_cast<Result*>(shared_memory_address_);
        GetIntegerv cmd;
        const GLuint kFBOClientTextureId = 4100;
        const GLuint kFBOServiceTextureId = 4101;

        // Register a texture id.
        EXPECT_CALL(*gl_, GenTextures(_, _))
            .WillOnce(SetArgPointee<1>(kFBOServiceTextureId))
            .RetiresOnSaturation();
        GenHelper<GenTexturesImmediate>(kFBOClientTextureId);

        // Setup "render to" texture.
        DoBindTexture(GL_TEXTURE_2D, kFBOClientTextureId, kFBOServiceTextureId);
        DoTexImage2D(
            GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0);
        DoBindFramebuffer(
            GL_FRAMEBUFFER, client_framebuffer_id_, kServiceFramebufferId);
        DoFramebufferTexture2D(GL_FRAMEBUFFER,
            GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D,
            kFBOClientTextureId,
            kFBOServiceTextureId,
            0,
            GL_NO_ERROR);

        result->size = 0;
        EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(0).RetiresOnSaturation();
        cmd.Init(GL_IMPLEMENTATION_COLOR_READ_FORMAT,
            shared_memory_id_,
            shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(1, result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());

        result->size = 0;
        EXPECT_CALL(*gl_, GetIntegerv(_, _)).Times(0).RetiresOnSaturation();
        cmd.Init(GL_IMPLEMENTATION_COLOR_READ_TYPE,
            shared_memory_id_,
            shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(1, result->GetNumResults());
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    // TODO(gman): PixelStorei

    // TODO(gman): SwapBuffers

} // namespace gles2
} // namespace gpu
